package cn.demoncat.util.mongo.util;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.ArrayUtils;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
import org.springframework.data.mongodb.core.query.BasicUpdate;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.Update.Position;

import cn.demoncat.util.lang.ReflectUtil;
import cn.demoncat.util.mongo.constant.MongoConstant;

/**
 * MongoDB更新工具（获取更新参数Update）
 *
 * @author 延晓磊
 *
 * @since 2017年7月22日
 */
public class UpdateUtil {

	/**
	 * 使用MongoDB的Update语句创建Update
	 * 
	 * @param json	JSON格式的Update语句，如"{$set:{'title':'MongoDB'}}"
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update json(String json){
		return new BasicUpdate(json);
	}
	
	/**
	 * 删除域
	 * 
	 * @param field 	域名，域不存在则忽略
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update delField(String field){
		//删除域：{ $unset : { field : 1} }
		return new Update().unset(field);
	}
	
	/**
	 * 删除域
	 * 
	 * @param update 	更新参数
	 * @param field 	域名，域不存在则忽略
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update delField(Update update, String field){
		//删除域：{ $unset : { field : 1} }
		return update.unset(field);
	}
	
	/**
	 * 重命名域
	 * 
	 * @param oldKeyName 域的旧名
	 * @param newKeyName 域的新名
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update renameField(String oldKeyName, String newKeyName){
		//重命名：{ $rename : { old_field : new_field } }
		return new Update().rename(oldKeyName, newKeyName);
	}
	
	/**
	 * 重命名域
	 * 
	 * @param update 		更新参数
	 * @param oldKeyName 	域的旧名
	 * @param newKeyName 	域的新名
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update renameField(Update update, String oldKeyName, String newKeyName){
		//重命名：{ $rename : { old_field : new_field } }
		return update.rename(oldKeyName, newKeyName);
	}
	
	/**
	 * 赋值
	 * 
	 * @param field 	域名：如果域不存在则添加
	 * @param value 	域值
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update set(String field, Object value){
		//更新：{ $set : { field : value } }
		return Update.update(field, value);
	}
	
	/**
	 * 赋值
	 * 
	 * @param update 	更新参数
	 * @param field 	域名：如果域不存在则添加
	 * @param value 	域值
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update set(Update update, String field, Object value){
		//更新：{ $set : { field : value } }
		return update.set(field, value);
	}
	
	/**
	 * 赋值
	 * 
	 * @param map 	域值对：如果域不存在则添加
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update set(Map<String,Object> map){
		//更新：{ $set : { field : value , field : value } }
		Update update = new Update();
		for (Entry<String, Object> entry : map.entrySet()) {
			if (!MongoConstant.ID_ATTR.equals(entry.getKey())) {
				update = update.set(entry.getKey(), entry.getValue());
			}
		}
		return update;
	}
	
	/**
	 * 赋值
	 * 
	 * @param update 	更新参数
	 * @param map 		域值对：如果域不存在则添加
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update set(Update update, Map<String,Object> map){
		//更新：{ $set : { field : value , field : value } }
		for (Entry<String, Object> entry : map.entrySet()) {
			if (!MongoConstant.ID_ATTR.equals(entry.getKey())) {
				update = update.set(entry.getKey(), entry.getValue());
			}
		}
		return update;
	}
	
	/**
	 * 赋值
	 * 
	 * @param o 	POJO（匹配非空属性）
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static <T> Update set(T o){
		Map<String, Object> map = ReflectUtil.toMap(o);
		Map<String, Object> map2 = new HashMap<>();
		for (Entry<String, Object> entry : map.entrySet()) {
			if (entry.getValue() != null) {
				map2.put(entry.getKey(),entry.getValue());
			}
		}
		return set(map2);
	}
	
	/**
	 * 赋值
	 * 
	 * @param update 	更新参数
	 * @param o 		POJO（匹配非空属性）
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static <T> Update set(Update update, T o){
		Map<String, Object> map = ReflectUtil.toMap(o);
		Map<String, Object> map2 = new HashMap<>();
		for (Entry<String, Object> entry : map.entrySet()) {
			if (entry.getValue() != null) {
				map2.put(entry.getKey(),entry.getValue());
			}
		}
		return set(update,map2);
	}
	
	/**
	 * 赋值/新建
	 * 
	 * @param field 域名：如果域存在则赋值，如果域不存在则新建文档
	 * @param value 域值
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update insert(String field, Object value){
		//设置或新增：$setOnInsert 
		return new Update().setOnInsert(field, value);
	}
	
	/**
	 * 赋值/新建
	 * 
	 * @param update 	更新参数
	 * @param field 	域名：如果域存在则赋值，如果域不存在则新建文档
	 * @param value 	域值
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update insert(Update update, String field, Object value){
		//设置或新增：$setOnInsert 
		return update.setOnInsert(field, value);
	}
	
	/**
	 * 递增
	 * 
	 * @param field 	域名，值必须为数值型
	 * @param inc 		递增值
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update inc(String field, Number inc){
		//递增：{ $inc : { field : 10} }
		return new Update().inc(field, inc);
	}
	
	/**
	 * 递增
	 * 
	 * @param update 	更新参数
	 * @param field 	域名，值必须为数值型
	 * @param inc 		递增值
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update inc(Update update, String field, Number inc){
		//递增：{ $inc : { field : 10} }
		return update.inc(field, inc);
	}
	
	/**
	 * 递增 1
	 * 
	 * @param field 	域名，值必须为数值型
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update inc(String field){
		//递增：{ $inc : { field : 1} }
		return new Update().inc(field, 1);
	}
	
	/**
	 * 递增 1
	 * 
	 * @param update 	更新参数
	 * @param field 	域名，值必须为数值型
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update inc(Update update, String field){
		//递增：{ $inc : { field : 1} }
		return update.inc(field, 1);
	}
	
	/**
	 * 递减 1
	 * 
	 * @param field 	域名，值必须为数值型
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update dec(String field){
		//递增：{ $inc : { field : -1} }
		return new Update().inc(field, -1);
	}
	
	/**
	 * 递减 1
	 * 
	 * @param update 	更新参数
	 * @param field 	域名，值必须为数值型
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update dec(Update update, String field){
		//递增：{ $inc : { field : -1} }
		return update.inc(field, -1);
	}
	
	/**
	 * 递乘
	 * 
	 * @param field 	域名，值必须为数值型
	 * @param inc 		递乘值
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update multiply(String field, Number inc){
		//递增：{ $mul : { field : 10} }
		return new Update().multiply(field, inc);
	}
	
	/**
	 * 递乘
	 * 
	 * @param update 	更新参数
	 * @param field 	域名，值必须为数值型
	 * @param inc 		递乘值
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update multiply(Update update, String field, Number inc){
		//递增：{ $mul : { field : 10} }
		return update.multiply(field, inc);
	}
	
	/**
	 * 追加到数组
	 * 
	 * @param field 	域名，值须为数组类型，如果域不存在则创建
	 * @param value 	将value追加到数组类型的域值中
	 * @return  
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update push(String field, Object... value){
		if (ArrayUtils.isEmpty(value)) {
			return new Update();
		}else if (value.length == 1) {
			//推：{ $push : { field : value } }
			return new Update().push(field,value);
		}else {
			//推：{ $push : { field : { $each : [ v1 , v2 ] } } }
			return new Update().push(field).each(value);
		}
	}
	
	/**
	 * 追加到数组
	 * 
	 * @param update 	更新参数
	 * @param field 	域名，值须为数组类型，如果域不存在则创建
	 * @param value 	将value追加到数组类型的域值中
	 * @return  
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update push(Update update, String field, Object... value){
		if (ArrayUtils.isEmpty(value)) {
			return update;
		}else if (value.length == 1) {
			//推：{ $push : { field : value } }
			return update.push(field,value);
		}else {
			//推：{ $push : { field : { $each : [ v1 , v2 ] } } }
			return update.push(field).each(value);
		}
	}
	
	/**
	 * 去重追加到数组
	 * 
	 * @param field 	域名，值须为数组类型，如果域不存在则创建
	 * @param value 	如果域值数组中不存在value元素则添加
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update add(String field, Object... value){
		if (ArrayUtils.isEmpty(value)) {
			return new Update();
		}else if (value.length == 1) {
			//去重添加：{ $addToSet : { field : value } }
			return new Update().addToSet(field,value);
		}else {
			//去重添加：{ $addToSet : { field : { $each : [ v1 , v2 ] } } }
			return new Update().addToSet(field).each(value);
		}
	}
	
	/**
	 * 去重追加到数组
	 * 
	 * @param update 	更新参数
	 * @param field 	域名，值须为数组类型，如果域不存在则创建
	 * @param value 	如果域值数组中不存在value元素则添加
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update add(Update update, String field, Object... value){
		if (ArrayUtils.isEmpty(value)) {
			return update;
		}else if (value.length == 1) {
			//去重添加：{ $addToSet : { field : value } }
			return update.addToSet(field,value);
		}else {
			//去重添加：{ $addToSet : { field : { $each : [ v1 , v2 ] } } }
			return update.addToSet(field).each(value);
		}
	}
	
	/**
	 * 从数组删除
	 * 
	 * @param field 	域名，值须为数组类型，如果域不存在则忽略
	 * @param value 	删除数组类型的域值中等于value的元素
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update pull(String field, Object... value){
		if (ArrayUtils.isEmpty(value)) {
			return new Update();
		}else if (value.length == 1) {
			//拉：{ $pull : { field : value } }
			return new Update().pull(field,value);
		}else {
			//拉：{ $pullAll : { field : [v1,v2] } }
			return new Update().pullAll(field,value);
		}
	}
	
	/**
	 * 从数组删除
	 * 
	 * @param update 	更新参数
	 * @param field 	域名，值须为数组类型，如果域不存在则忽略
	 * @param value 	删除数组类型的域值中等于value的元素
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update pull(Update update, String field, Object... value){
		if (ArrayUtils.isEmpty(value)) {
			return update;
		}else if (value.length == 1) {
			//拉：{ $pull : { field : value } }
			return update.pull(field,value);
		}else {
			//拉：{ $pullAll : { field : [v1,v2] } }
			return update.pullAll(field,value);
		}
	}
	
	/**
	 * 从数组删除1个
	 * 
	 * @param field		域名，值须为数组类型，如果域不存在则忽略
	 * @param last		true删除末尾元素，false删除头部元素
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Update pop(String field, boolean last) {
		return new Update().pop(field, last ? Position.LAST : Position.FIRST);
	}
	
	/**
	 * 从数组删除1个
	 * 
	 * @param update 	更新参数
	 * @param field		域名，值须为数组类型，如果域不存在则忽略
	 * @param last		true删除末尾元素，false删除头部元素
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Update pop(Update update, String field, boolean last) {
		return update.pop(field, last ? Position.LAST : Position.FIRST);
	}
	
	/**
	 * 当前时间
	 * 
	 * @param field 	域名，值须为时间戳，域值将设置为当前日期
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update now(String field){
		//当前日期：$currentDate 
		return new Update().currentDate(field);
	}
	
	/**
	 * 当前时间
	 * 
	 * @param update 	更新参数 
	 * @param field 	域名，值须为时间戳，域值将设置为当前日期
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update now(Update update, String field){
		//当前日期：$currentDate 
		return update.currentDate(field);
	}
	
	/**
	 * 最大值
	 * 
	 * @param field 	域名：如果域不存在则添加
	 * @param value 	域值：如果大于当前值则设置
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update max(String field, Object value){
		//最大值：$max
		return new Update().max(field, value);
	}
	
	/**
	 * 最大值
	 * 
	 * @param update 	更新参数
	 * @param field 	域名：如果域不存在则添加
	 * @param value 	域值：如果大于当前值则设置
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update max(Update update, String field, Object value){
		//最大值：$max
		return update.max(field, value);
	}
	
	/**
	 * 最小值
	 * 
	 * @param field 	域名：如果域不存在则添加
	 * @param value 	域值：如果小于当前值则设置
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update min(String field, Object value){
		//最小值：$min
		return new Update().min(field, value);
	}
	
	/**
	 * 最小值
	 * 
	 * @param update 	更新参数
	 * @param field 	域名：如果域不存在则添加
	 * @param value 	域值：如果小于当前值则设置
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Update min(Update update, String field, Object value){
		//最小值：$min
		return update.min(field, value);
	}
	
	/**
	 * 聚合
	 * 
	 * @param field		域名：如果域不存在则添加
	 * @param value		域值/聚合命令
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static AggregationUpdate aggregation(String field, Object value){
		// 聚合更新
		return Aggregation.newUpdate().set(field).toValue(value);
	}
	
	/**
	 * 聚合
	 * 
	 * @param field		域名：如果域不存在则添加
	 * @param value		域值/聚合命令
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static AggregationUpdate aggregation(AggregationUpdate update, String field, Object value){
		// 聚合更新
		return update.set(field).toValue(value);
	}
	
}
